import java.io.*;
import java.text.DecimalFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import libsvm.*;

public class EmoDetect_m {
	
	public static HashSet<String> stopwords = new HashSet<String>();//停用词（包括英文字母)
	public static HashSet<String> senti = new HashSet<String>();//情感词典
	public static HashMap<Integer,String> test = new HashMap<Integer,String>();//将过滤到路径信息的文本写入test
	public static HashMap<Integer,String> svmtest = new HashMap<Integer,String>();//存放svmtest特征文本
	public static List<String> result = new ArrayList<String>();//存放result
	public static List<String> resultlist = new ArrayList<String>();//存放result+sen;送入规则处理
	public static List<String> newresultlist = new ArrayList<String>();//规则处理后的结果
	public static HashMap<Integer,String> fea_role = new HashMap<Integer,String>();
	public static HashMap<Integer,String> fea_senti = new HashMap<Integer,String>();
	public static HashMap<String, Double> gramIDF= new HashMap<String, Double>(); //存放所有gram和gram2的IDF
	public static HashMap<String, Integer> gramIndex= new HashMap<String, Integer>(); //存放所有gram和gram2和其index
	
	DecimalFormat fnum = new DecimalFormat("##0.000"); 
	
	public void readDic() throws Exception{//生成stopwords, senti, gramIDF, gramIndex, gram2IDF，gram2Index
		
		InputStreamReader read = new InputStreamReader(new FileInputStream("dictionary"),"utf-8");
		@SuppressWarnings("resource")
		BufferedReader reader = new BufferedReader(read);
		String temp ="";
		while((temp = reader.readLine()) != null){
			String[] tokens = temp.split("\t");
//			System.out.println(temp);
			String cate = tokens[0];
			String item = tokens[1];
			switch(cate){
				case "st"://代表停用词
					stopwords.add(item);
					break;
				case "se"://代表情感词
					senti.add(item);
					break;
				case "g":
					String[] gramtokens = item.split(" ");
					Integer index = Integer.valueOf(gramtokens[0]);
					String gram = gramtokens[1];
					Double gramidf = Double.valueOf(gramtokens[2]);
					gramIDF.put(gram, gramidf);
					gramIndex.put(gram, index);
					break;
			}
		}
	}
	
	public String getSentiFea(String line) throws Exception{
		
		String feasenti = "";
		for(String sentiline : senti){
//		while((temp = reader.readLine()) != null){
			String[] tokens = sentiline.split(" ");
			String index = tokens[0];
			String lex = tokens[1];
			String value = tokens[2];
			if(line.contains(lex)){
				feasenti = feasenti + index + ":" + value + " ";
			}
		}
		return feasenti;
	}
	
	public void readFile(String file) throws Exception{//读取文本，同时获取角色、情感特征
		
		InputStreamReader read = new InputStreamReader(new FileInputStream(file),"gbk");
		@SuppressWarnings("resource")
		BufferedReader reader = new BufferedReader(read);
		String testtemp = "";
		String content = "";
		String role = "";
		String senti = "";
		int index = 0;
		while((testtemp = reader.readLine()) != null){			
			if (testtemp.substring(0, 1).contains("/")){//过滤路径行
				continue;
			}
			String[] tokens = testtemp.split("	");
			content = tokens[0].replace(" ", "");	//去掉空格
//			role = tokens[3];
			senti = getSentiFea(content);
/*			if(role.contains("客户")){
				fea_role.put(index, "1:2");
			}else{
				fea_role.put(index, "1:1");
			}*/
			fea_role.put(index, "1:2");
			test.put(index, content);	
			fea_senti.put(index, senti);
			index += 1;						
		}
	}
	
	public void FormateData() throws IOException{
		
//		File tetsfile = new File("G:\\Feature");
//		Writer testwriter = new OutputStreamWriter(new FileOutputStream(tetsfile),"gbk");//将结果写入指定路径
		
		String content = "";
		int index = 0;
		Iterator<Integer> iter = test.keySet().iterator();
		while (iter.hasNext()) {
			index = iter.next();
			content = test.get(index);
//			testwriter.write(content + '\n');
			HashMap<String, Integer> gramMap = new HashMap<String, Integer>();//gramMap存放每句话的gram和gram2信息			
			for(int i= 0; i<content.length()-1; i++){
				String gram = content.substring(i, i+1);
				if(stopwords.contains(gram)==false && gram.length()>0 && gram.trim().isEmpty()==false){
					if(gramMap.containsKey(gram)){
						gramMap.put(gram, gramMap.get(gram)+1);
					}else{
						gramMap.put(gram, 1);
					}
				}
				String gram2 = content.substring(i, i+2);//word为2gram
				if(stopwords.contains(gram2)==false && gram2.length()>0 && gram2.trim().isEmpty()==false){
					if(gramMap.containsKey(gram2)){
						gramMap.put(gram2, gramMap.get(gram2)+1);
					}else{
						gramMap.put(gram2, 1);
					}			
				}
			}			
			HashMap<Integer, Double> gramTfIdf = new HashMap<Integer, Double>();
			Iterator<String> grammap = gramMap.keySet().iterator();
			while(grammap.hasNext()){
				String gram = grammap.next();
				if(gramIndex.containsKey(gram) && gramIDF.containsKey(gram)){
					double tf = (double)(gramMap.get(gram)) / (content.length() - 1);
					double idf = gramIDF.get(gram);
					double tfidf = tf * idf ;
					gramTfIdf.put(gramIndex.get(gram), tfidf);
				}
			}
			String fea_word = "";
		    Object[] key =  gramTfIdf.keySet().toArray();
		    Arrays.sort(key); 
		    for(int i = 0; i<key.length; i++)  {    
		    	fea_word = fea_word +  String.valueOf(key[i]) + ":" +  String.valueOf(gramTfIdf.get(key[i])) + " ";    		    	
		    } 
//		    fea_2gram.put(index, fea_word);
//			System.out.println(fea_word);
		    String fea_all = fea_role.get(index) + " " + fea_senti.get(index) + fea_word;
		    svmtest.put(index, fea_all);
//			testwriter.write(fea_all + '\n');
		}
		System.out.println("Feature Completed !!");
//		testwriter.close();
	}
	
	public String predict(String line, svm_model model, int predict_probability) throws IOException{
		
		String output = "";
		int svm_type=svm.svm_get_svm_type(model);
		int nr_class=svm.svm_get_nr_class(model);
		double[] prob_estimates=null;
		
		if(predict_probability == 1){
			int[] labels=new int[nr_class];
			svm.svm_get_labels(model,labels);
			prob_estimates = new double[nr_class];
		}
		
		StringTokenizer st = new StringTokenizer(line," \t\n\r\f:");
		
		int m = st.countTokens()/2;
		svm_node[] x = new svm_node[m];
		for(int j=0;j<m;j++){
			x[j] = new svm_node();
			x[j].index = Integer.parseInt(st.nextToken());
			x[j].value = Double.valueOf(st.nextToken()).doubleValue();
		}
		
		double v;
		if (predict_probability==1 && (svm_type==svm_parameter.C_SVC || svm_type==svm_parameter.NU_SVC)){
			v = svm.svm_predict_probability(model,x,prob_estimates);
			output = v+" ";
			for(int j=0;j<nr_class;j++)
				output= output + prob_estimates[j]+" ";
//			output = output + "\n";
		}else{
			v = svm.svm_predict(model,x);
			output= v+"\n";
		}		
		return output;
	}
	
	public List<String> multipredict(List<Map.Entry<Integer, String>> list, Integer treadcount) throws Exception{
		
		final svm_model model = svm.svm_load_model("model");
		HashMap<Integer, String> resultTemp = new HashMap<Integer, String>();//存放预测结果
		List<String> resultLs = new ArrayList<String>();//存放预测结果
		
//		int threadCounts = 8;//此处定义使用的线程数    
		int threadCounts = treadcount;//此处定义使用的线程数  
		ExecutorService exec=Executors.newFixedThreadPool(threadCounts);//根据定义的线程数目进行开辟线程
		List<Callable<HashMap<Integer,String>>> callList=new ArrayList<Callable<HashMap<Integer,String>>>();//生成很大的List
		
		int len=list.size()/threadCounts;//根据线程数目，平均分割List，这是每段sublist的大小		
		if(len==0){//List中的数量没有线程数多（很少存在）
			threadCounts=list.size();//采用一个线程处理List中的一个元素
			len=list.size()/threadCounts;//重新平均分割List
		}//此处是为了防止线程数目大于list的大小，可不用管
//		long startTime = System.currentTimeMillis();//程序开始时间，可去掉
		for(int i=0;i<threadCounts;i++){//这个for循环是为了启动各个线程，并且定义每个线程要处理的内容						
			final List<Entry<Integer, String>> subList;
			if(i==threadCounts-1){
				subList=list.subList(i*len,list.size());
			}else{
				subList=list.subList(i*len, len*(i+1)>list.size()?list.size():len*(i+1));
			}//此处if-else是为了在for循环中均分前面定义的list
			callList.add(new Callable<HashMap<Integer,String>>(){
				public HashMap<Integer,String> call() throws Exception {
//					List<Entry<Integer, String>> alist = new ArrayList<Entry<Integer, String>>();
					HashMap<Integer,String> alistMap = new HashMap<Integer, String>();
					for(Entry<Integer, String> i:subList){
						int index = i.getKey();
						String line = i.getValue();
//						System.out.println(line);
						alistMap.put(index, predict(line, model, 1));//对每一句话进行预测
//						alistMap.put(index, line);
//						alist = new ArrayList<Map.Entry<Integer, String>>(alistMap.entrySet());
					}
					return alistMap;//此处将上述处理得到的结果进行返回，并保存到callList中
				}
			});
		}
		//等待所有的for循环处理完毕，将每个线程中的结果集合进行汇总
		List<Future<HashMap<Integer, String>>> futureList=exec.invokeAll(callList);
//		long endtime = System.currentTimeMillis();
//		System.err.println("-------------------------------------------------------");
//		System.out.print(endtime-startTime);
		for(Future<HashMap<Integer, String>> future:futureList){
			resultTemp.putAll(future.get());
		}		
		Object[] key =  resultTemp.keySet().toArray();//将结果进行排序，以免组合sublist时乱序
		Arrays.sort(key); 
	    for(int i = 0; i<key.length; i++)  {    
	    	resultLs.add(resultTemp.get(key[i])); 
//	    	System.out.println(resultTemp.get(key[i]));
	    }		
		exec.shutdown();//结束线程集合
		
		return resultLs;
	}
	
	public void Predict(Integer treadcount) throws Exception{	
		
//		String[] parg = {"-b","1","svm.test","model2_gailv","result"};//加上-b 1 输出概率
//		svm_predict.main(parg);	
//		System.out.println("预测结束！");	
		List<Map.Entry<Integer, String>> svmtestList = new ArrayList<Map.Entry<Integer, String>>(svmtest.entrySet());
		result = multipredict(svmtestList, treadcount);	
//		System.out.println(result.size());
	}
	
	public void conbineResult(String path) throws Exception{
		
//		File tetsfile = new File(outpath);
//		Writer testwriter = new OutputStreamWriter(new FileOutputStream(tetsfile),"gbk");//将结果写入指定路径
		
		InputStreamReader read = new InputStreamReader(new FileInputStream(path),"gbk");
		@SuppressWarnings("resource")
		BufferedReader reader = new BufferedReader(read);
		String testtemp = "";
		int i = 0;
		while((testtemp = reader.readLine()) != null){	
			if (testtemp.substring(0, 1).contains("/")){
//			testwriter.write(testtemp + '\n');
			resultlist.add(testtemp + '\n');
			continue;
			}
			String re = result.get(i);
//			double P1 = Double.valueOf(re.split(" ")[1]);
			double P2 = Double.valueOf(re.split(" ")[2]);
//			double P3 = Double.valueOf(re.split(" ")[3]);
			String lable = re.split(" ")[0];
			switch(lable){
			case "1.0":	
//				testwriter.write(testtemp + "	" + "[Happy]" +"	" + fnum.format(P1) + "\n");
				resultlist.add(testtemp.split("\t")[1] + "	" + "[Happy]" +"	" + fnum.format(P2) + "\n");
				break;
			case "2.0":
//				testwriter.write(testtemp + "	" + "[Angry]" +"	" + fnum.format(P2) + "\n");
				resultlist.add(testtemp.split("\t")[1] + "	" + "[Angry]" +"	" + fnum.format(P2) + "\n");
				break;
			case "3.0":
//				testwriter.write(testtemp + "	" + "[Neu]" +"	" + fnum.format(P3) + "\n");
				resultlist.add(testtemp.split("\t")[1] + "	" + "[Neu]" +"	" + fnum.format(P2) + "\n");
				break;
			}
			i = i + 1;
		}		
//		testwriter.close();		
	}
	
	public void addLingRule(){//加入语言规则
		for(int i=0; i<resultlist.size(); i++){
			if(resultlist.get(i).contains("投诉") && resultlist.get(i).contains("	坐席")){//规则1：凡是坐席说的“投诉”，词句均判断为中性
				String[] tokens = resultlist.get(i).split("\t");
				tokens[4] = "[Neu]";
				tokens[5] = "0.999";
				String newresultstring = tokens[0] + "\t" + tokens[1] + "\t" + tokens[2] + "\t" + tokens[3] + "\t" + tokens[4] + "\t" + tokens[5] + "\n";
				newresultlist.add(newresultstring);
			}else{
				newresultlist.add(resultlist.get(i));
			}
		}
	}
	
	public void writeResult(String outpath) throws Exception{
		
		File tetsfile = new File(outpath);
		Writer testwriter = new OutputStreamWriter(new FileOutputStream(tetsfile),"gbk");//将结果写入指定路径
		
		for(int i=0; i<newresultlist.size(); i++){
			testwriter.write(newresultlist.get(i));
		}
		testwriter.close();	
	}
	
	
	public void emotionDetect(Integer treadcount, String path,String outpath) throws Exception{

		readDic();
		readFile(path);
		FormateData();             
		Predict(treadcount);
		conbineResult(path);//合并模型预测结果
		addLingRule();//加入语言规则
		writeResult(outpath);
		System.out.println("Predict result:	" + outpath);
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub		
		try {
			long starttime = System.currentTimeMillis();
			EmoDetect_m ed = new EmoDetect_m();
//			ed.emotionDetect(2, "G:\\test","G:\\test_emotion");
//			ed.emotionDetect(4,"G:\\tbnr_Result.txt","G:\\emotion_tbnr_Result.txt");
			ed.emotionDetect(Integer.valueOf(args[0]),args[1],args[2]);
			long endtime = System.currentTimeMillis();
			System.err.println(endtime - starttime);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
